package com.guoguo.cloudmusic.logic

import androidx.lifecycle.liveData
import com.guoguo.cloudmusic.logic.dao.UserInfoDao
import com.guoguo.cloudmusic.logic.network.ApplicationNetWork
import com.guoguo.cloudmusic.ui.login.LoginUser
import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext

/**
 * 仓库数据管理类
 * @author GuoGuo
 * @date 2021/12/15
 */
object Repository {
    /**
     *，由于在Repository封装的每个网络请求接口都可能会抛出异常，
     * 于是我们必须在仓库层中为每个网络请求都进行try catch处理，这无疑增加了仓库层代码实现的复杂度。
     * 然而之前我就说过，其实完全可以在某个统一的入口函数中进行封装，使得只要进行一次try catch处理就行了
     * fire函数
     * 这是一个按照liveData()函数的参数接收标准定义的一个高阶函数。在fire()函数的内部会先调用一下liveData()函数，
     * 然后在liveData()函数的代码块中统一进行了try catch处理，并在try语句中调用传入的Lambda表达式中的代码，
     * 最终获取Lambda表达式的执行结果并调用emit()方法发射出去。另外还有一点需要注意，在liveData()函数的代码块中，
     * 我们是拥有挂起函数上下文的，可是当回调到Lambda表达式中，代码就没有挂起函数上下文了，但实际上Lambda表达式中的代码一定也是在挂起函数中运行的。
     * 为了解决这个问题，我们需要在函数类型前声明一个suspend关键字，以表示所有传入的Lambda表达式中的代码也是拥有挂起函数上下文的。
     * 定义好了fire()函数之后，剩下的工作就很简单了。只需要分别将searchPlaces()和refreshWeather()方法中调用的liveData()函数替换成fire()函数，
     * 然后把诸如try catch语句、emit()方法之类的逻辑移除即可。这样，仓库层中的代码就变得更加简洁清晰了。
     *
     */
    private fun <T> fire(context: CoroutineContext, block: suspend () -> Result<T>) =
        liveData<Result<T>>(context) {
            val result = try {
                block()
            } catch (e: Exception) {
                Result.failure<T>(e)
            }
            emit(result)
        }

    //登录
    fun login(data: LoginUser) = fire(Dispatchers.IO) {
        val res = ApplicationNetWork.login(data)
        if (res.code == 200) {
            // 异步线程
            Thread {
                UserInfoDao.saveUserInfo(res)
            }.start()
            Result.success(res.account)
        } else Result.failure(RuntimeException("response status is ${res.code}"))
    }

    //搜索
    fun search(data: String) = fire(Dispatchers.IO) {
        val res = ApplicationNetWork.search(data)
        if (res.code == 200) Result.success(res.result.songs)
        else Result.failure(RuntimeException("response status is ${res.code}"))
    }

    //获取用户歌单
    fun getPlayList(data: String) = fire(Dispatchers.IO) {
        val res = ApplicationNetWork.getPlayList(data)
        if (res.code == 200) Result.success(res.playlist)
        else Result.failure(RuntimeException("response status is ${res.code}"))
    }

    //获取歌单所有歌曲
    fun getPlayListDetail(data: String) = fire(Dispatchers.IO) {
        val res = ApplicationNetWork.getPlayListTrack(data)
        if (res.code == 200) Result.success(res.songs)
        else Result.failure(RuntimeException("response status is ${res.code}"))
    }

    //获取每日推荐歌曲
    fun getRecommendSongs() = fire(Dispatchers.IO) {
        val res = ApplicationNetWork.getRecommendSongs()
        if (res.code == 200) Result.success(res.data.dailySongs)
        else Result.failure(RuntimeException("response status is ${res.code}"))
    }

    //获取歌词
    fun getLyric(id: Int) = fire(Dispatchers.IO) {
        val res = ApplicationNetWork.getLyric(id)
        if (res.code == 200) Result.success(res)
        else Result.failure(RuntimeException("response status is ${res.code}"))
    }

    //获取音乐播放路径
    fun getMusicUrl(id:String) = fire(Dispatchers.IO){
        val res = ApplicationNetWork.getMusicUrl(id)
        if (res.code == 200) Result.success(res)
        else Result.failure(RuntimeException("response status is ${res.code}"))
    }

    //获取评论
    fun getMusicComment(id:String) = fire(Dispatchers.IO){
        val res = ApplicationNetWork.getMusicComment(id)
        if (res.code == 200) Result.success(res)
        else Result.failure(RuntimeException("response status is ${res.code}"))
    }

    //获取评论
    fun getTopList() = fire(Dispatchers.IO){
        val res = ApplicationNetWork.getTopList()
        if (res.code == 200) Result.success(res)
        else Result.failure(RuntimeException("response status is ${res.code}"))
    }

//    fun refreshWeather(lng: String, lat: String) = fire(Dispatchers.IO) {
//        coroutineScope {
//            val deferredRealtime = async {
//                SunnyWeatherNetwork.getRealtimeWeather(lng, lat)
//            }
//            val deferredDaily = async {
//                SunnyWeatherNetwork.getDailyWeather(lng, lat)
//            }
//            val realtimeResponse = deferredRealtime.await()
//            val dailyResponse = deferredDaily.await()
//            if (realtimeResponse.status == "ok" && dailyResponse.status == "ok") {
//                val weather = Weather(realtimeResponse.result.realtime,
//                    dailyResponse.result.daily)
//                Result.success(weather)
//            } else {
//                Result.failure(
//                    RuntimeException(
//                        "realtime response status is ${realtimeResponse.status}" +
//                                "daily response status is ${dailyResponse.status}"
//                    )
//                )
//            }
//        }
//    }
}